package com.atguigu.gmall.list.service.impl;

import com.atguigu.gmall.model.list.SearchAttr;
import com.atguigu.gmall.model.vo.SearchAttrListVo;
import com.atguigu.gmall.model.vo.TrademarkVo;
import com.google.common.collect.Lists;
import com.atguigu.gmall.model.vo.OrderMapVo;

import com.atguigu.gmall.list.mapper.GoodsMapper;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.Goods;
import com.atguigu.gmall.model.list.SearchParam;
import com.atguigu.gmall.model.vo.GoodsSearchResultVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.alibaba.nacos.common.http.HttpUtils.buildUrl;
import static org.elasticsearch.rest.action.cat.RestTable.buildResponse;

/**
 * @Author 卡尼奶
 * @Date 2022/5/28 14:24
 * @Version 1.0
 */
@Service
@Slf4j
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private ElasticsearchRestTemplate restTemplate;

    @Override
    public void saveSearch(Goods goods) {
        goodsMapper.save(goods);
    }

    @Override
    public void deleteSearchProduct(Long id) {
        goodsMapper.deleteById(id);
    }

    @Override
    public GoodsSearchResultVo searchGoods(SearchParam param) {

        //根据前端传递来的参数，构造复杂的检索条件
        Query query = buildQueryBySearchParam(param);

        //检索
        SearchHits<Goods> goods = restTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));
        //数据提取
        GoodsSearchResultVo resultVo = buildResponse(goods, param);
        return resultVo;
    }

    @Override
    public void incrHotScore(Long skuId, Long score) {
        Optional<Goods> byId = goodsMapper.findById(skuId);
        Goods goods = byId.get();
        goods.setHotScore(score);
        goodsMapper.save(goods);
    }

    /**
     * 根据前端传递的复杂检索条件，构造自己的Query条件
     *
     * @param param
     * @return
     */
    public Query buildQueryBySearchParam(SearchParam param) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        NativeSearchQuery query = null;
        //三级分类
        if (param.getCategory1Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category1Id", param.getCategory1Id()));
        }
        if (param.getCategory2Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category2Id", param.getCategory2Id()));
        }
        if (param.getCategory3Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category3Id", param.getCategory3Id()));
        }

        //品牌
        if (!StringUtils.isEmpty(param.getTrademark())) {
            String[] split = param.getTrademark().split(":");
            boolQuery.must(QueryBuilders.termQuery("tmId", split[0]));
        }

        //平台属性 props=23:4G:运行内存
        String[] props = param.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                String[] split = prop.split(":");
                BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
                //属性id
                queryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                //属性值
                queryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));

                boolQuery.must(QueryBuilders.nestedQuery("attrs", queryBuilder, ScoreMode.None));
            }
        }

        //模糊搜索
        if (!StringUtils.isEmpty(param.getKeyword())) {
            boolQuery.must(QueryBuilders.matchQuery("title", param.getKeyword()));
        }

        query = new NativeSearchQuery(boolQuery);

        //排序  1:desc 2:asc
        String order = param.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = order.split(":");
            String orderName = "";
            switch (split[0]) {
                case "1":
                    orderName = "hotScore";
                    break;
                case "2":
                    orderName = "price";
                    break;
                default:
                    orderName = "hotScore";
            }
            query.addSort(Sort.by(Sort.Direction.fromString(split[1]), orderName));
        }

        if (!StringUtils.isEmpty(param.getKeyword())) {
            //高亮显示
            HighlightBuilder builder = new HighlightBuilder();
            builder.field("title").preTags("<span style='color:red'>").postTags("</span>");
            HighlightQuery highlightQuery = new HighlightQuery(builder);
            query.setHighlightQuery(highlightQuery);
        }

        //分页
        Pageable pageable = PageRequest.of(param.getPageNo() - 1, param.getPageSize());
        query.setPageable(pageable);

        List<AbstractAggregationBuilder> aggregationBuilders = new ArrayList<>();

        //品牌
        TermsAggregationBuilder tmIdAgg = AggregationBuilders.terms("tmIdAgg");
        tmIdAgg.field("tmId").size(100);
        //子聚合品牌名
        TermsAggregationBuilder tmNameAgg = AggregationBuilders.terms("tmNameAgg");
        tmNameAgg.field("tmName").size(1);
        tmIdAgg.subAggregation(tmNameAgg);
        //子聚合品牌logo
        TermsAggregationBuilder tmLogoUrlAgg = AggregationBuilders.terms("tmLogoUrlAgg");
        tmLogoUrlAgg.field("tmLogoUrl").size(1);
        tmIdAgg.subAggregation(tmLogoUrlAgg);
        aggregationBuilders.add(tmIdAgg);

        //平台属性
        NestedAggregationBuilder nested = AggregationBuilders.nested("attrsAgg", "attrs");
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(100);
        //品牌名称
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(1);
        attrIdAgg.subAggregation(attrNameAgg);
        //品牌值
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(100);
        attrIdAgg.subAggregation(attrValueAgg);

        nested.subAggregation(attrIdAgg);
        aggregationBuilders.add(nested);

        query.setAggregations(aggregationBuilders);

        return query;
    }

    /**
     * 根据检索结果构造响应数据
     *
     * @param goods
     * @return
     */
    private GoodsSearchResultVo buildResponse(SearchHits<Goods> goods, SearchParam param) {
        GoodsSearchResultVo resultVo = new GoodsSearchResultVo();
        resultVo.setSearchParam(param);
        //品牌面包屑 trademark=2:华为
        String trademark = param.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] split = trademark.split(":");
            resultVo.setTrademarkParam("品牌：" + split[1]);
        }
        //url
        String path = buildUrl(param);
        resultVo.setUrlParam(path);

        //平台属性
        List<SearchAttrListVo> searchAttrListVos = analyseAttrList(goods);
        resultVo.setAttrsList(searchAttrListVos);

        //品牌集合
        List<TrademarkVo> trademarkList = analyseTrademarkList(goods);
        resultVo.setTrademarkList(trademarkList);

        //平台属性面包屑 23:4G:运行内存
        List<SearchAttr> propsParamList = new ArrayList<>();
        if (param.getProps() != null && param.getProps().length > 0) {
            for (String prop : param.getProps()) {
                String[] split = prop.split(":");
                SearchAttr searchAttr = new SearchAttr();
                searchAttr.setAttrId(Long.valueOf(split[0]));
                searchAttr.setAttrValue(split[1]);
                searchAttr.setAttrName(split[2]);
                propsParamList.add(searchAttr);
            }
            resultVo.setPropsParamList(propsParamList);
        }

        //排序 1:asc 2:desc
        String order = param.getOrder();
        if (!StringUtils.isEmpty(order)){
            String[] split = order.split(":");
            OrderMapVo orderMapVo = new OrderMapVo();
            orderMapVo.setType(split[0]);
            orderMapVo.setSort(split[1]);
            resultVo.setOrderMap(orderMapVo);
        }


        //商品列表
        List<Goods> list = new ArrayList<>();
        for (SearchHit<Goods> good : goods) {
            Goods content = good.getContent();
            List<String> title = good.getHighlightField("title");
            if (!StringUtils.isEmpty(param.getKeyword())){
                content.setTitle(title.get(0));
            }
            list.add(content);
        }
        resultVo.setGoodsList(list);

        //当前页
        resultVo.setPageNo(param.getPageNo());

        //总页数
        Long l = goods.getTotalHits() % param.getPageSize() == 0 ? goods.getTotalHits() / param.getPageSize() : goods.getTotalHits() / param.getPageSize() + 1;
        resultVo.setTotalPages(l.intValue());

        return resultVo;
    }

    private String buildUrl(SearchParam param) {
        StringBuilder sb = new StringBuilder("list.html?");
        if (param.getCategory1Id() != null) {
            sb.append("category1Id=" + param.getCategory1Id() + "&");
        }

        if (param.getCategory2Id() != null) {
            sb.append("category2Id=" + param.getCategory2Id() + "&");
        }

        if (param.getCategory3Id() != null) {
            sb.append("category3Id=" + param.getCategory3Id() + "&");
        }

        if (!StringUtils.isEmpty(param.getTrademark())) {
            sb.append("trademark=" + param.getTrademark() + "&");
        }

        if (!StringUtils.isEmpty(param.getKeyword())) {
            sb.append("keyword=" + param.getKeyword() + "&");
        }

        //props=23:4G:运行内存
        if (param.getProps() != null && param.getProps().length > 0) {
            for (String prop : param.getProps()) {
                sb.append("props=" + prop + "&");
            }
        }
        return sb.toString();

    }

    /**
     * 根据检索到的整个结果，分析出平台属性列表信息
     *
     * @param goods
     * @return
     */
    private List<SearchAttrListVo> analyseAttrList(SearchHits<Goods> goods) {
        List<SearchAttrListVo> propsParamList = new ArrayList<>();
        //获取所有聚合结果
        Aggregations aggregations = goods.getAggregations();
        //拿到嵌入聚合结果
        ParsedNested parsedNested = aggregations.get("attrsAgg");
        ParsedLongTerms attrsAgg = parsedNested.getAggregations().get("attrIdAgg");
        for (Terms.Bucket bucket : attrsAgg.getBuckets()) {
            SearchAttrListVo searchAttr = new SearchAttrListVo();
            Number keyAsNumber = bucket.getKeyAsNumber();
            searchAttr.setAttrId(keyAsNumber.longValue());

            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            String keyAsAttrNameString = attrNameAgg.getBuckets().get(0).getKeyAsString();
            searchAttr.setAttrName(keyAsAttrNameString);

            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<String> attrValueList = new ArrayList<>();
            for (Terms.Bucket valueAggBucket : attrValueAgg.getBuckets()) {
                String keyAsString = valueAggBucket.getKeyAsString();
                attrValueList.add(keyAsString);
            }
            searchAttr.setAttrValueList(attrValueList);

            propsParamList.add(searchAttr);
        }
        return propsParamList;
    }

    /**
     * 根据检索到的整个结果，分析出品牌列表信息
     *
     * @param goods
     * @return
     */
    private List<TrademarkVo> analyseTrademarkList(SearchHits<Goods> goods) {
        List<TrademarkVo> list = new ArrayList<>();

        //获取所有聚合结果
        Aggregations aggregations = goods.getAggregations();
        //根据名称获取聚合结果
        ParsedLongTerms tmIdAgg = aggregations.get("tmIdAgg");
        for (Terms.Bucket bucket : tmIdAgg.getBuckets()) {
            Number number = bucket.getKeyAsNumber();
            TrademarkVo trademarkVo = new TrademarkVo();
            trademarkVo.setTmId(number.longValue());

            ParsedStringTerms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
            String keyAsString = tmNameAgg.getBuckets().get(0).getKeyAsString();
            trademarkVo.setTmName(keyAsString);

            ParsedStringTerms tmLogoUrlAgg = bucket.getAggregations().get("tmLogoUrlAgg");
            String keyAsLogoString = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            trademarkVo.setTmLogoUrl(keyAsLogoString);

            list.add(trademarkVo);
        }

        return list;
    }
}
